package component

import (
	"fmt"
	"sync"
)

type Componentry struct {
	components map[string]Component // compID:component

	instNames   []string
	instConfigs map[string]*ComponentConfig
	instances   map[string]interface{}

	mu sync.RWMutex
}

func NewComponentry() *Componentry {
	return &Componentry{
		components:  make(map[string]Component),
		instNames:   make([]string, 0),
		instConfigs: make(map[string]*ComponentConfig),
		instances:   make(map[string]interface{}),
	}
}

func (c *Componentry) RegisterComponent(comp Component) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.components[comp.ID()]; ok {
		return fmt.Errorf("Component `%v` already registered", comp.ID())
	}
	c.components[comp.ID()] = comp
	return nil
}

func (c *Componentry) Init(instConfigs []*ComponentConfig) error {
	for _, instConfig := range instConfigs {
		_, err := c.CreateInstance(instConfig)
		if err != nil {
			return err
		}
	}
	for _, instName := range c.instNames {
		if e := c.InitInstance(instName); e != nil {
			return e
		}
	}
	return nil
}

func (c *Componentry) CreateInstance(instConfig *ComponentConfig) (interface{}, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if comp, ok := c.components[instConfig.ID]; ok {
		if _, ok := c.instConfigs[instConfig.Name]; ok {
			return c.instances[instConfig.Name], fmt.Errorf("Component Inst `%v` already created", instConfig.Name)
		}

		instance, err := comp.Create(instConfig)
		if err == nil {
			c.instNames = append(c.instNames, instConfig.Name)
			c.instConfigs[instConfig.Name] = instConfig
			c.instances[instConfig.Name] = instance
		}
		return instance, err
	}

	return nil, fmt.Errorf("Component `%v` Not Registered", instConfig.Name)
}

func (c *Componentry) Instance(name string) interface{} {
	c.mu.RLock()
	defer c.mu.RUnlock()
	if inst, ok := c.instances[name]; ok {
		return inst
	}
	return nil
}

func (c *Componentry) InitInstance(name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if inst, ok := c.instances[name]; ok {
		instConfig := c.instConfigs[name]
		return c.components[instConfig.ID].Init(inst, instConfig)
	}
	return fmt.Errorf("Component Inst `%v` Not created", name)
}

func (c *Componentry) StartInstance(name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if inst, ok := c.instances[name]; ok {
		instConfig := c.instConfigs[name]
		return c.components[instConfig.ID].Start(inst)
	}
	return fmt.Errorf("Component Inst `%v` Not created", name)
}

func (c *Componentry) StopInstance(name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if inst, ok := c.instances[name]; ok {
		instConfig := c.instConfigs[name]
		return c.components[instConfig.ID].Stop(inst)
	}
	return fmt.Errorf("Component Inst `%v` Not created", name)
}

func (c *Componentry) UpdateInstance(name string, cfg *ComponentConfig) (interface{}, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if inst, ok := c.instances[name]; ok {
		c.instConfigs[name] = cfg
		newInst, err := c.components[cfg.ID].Update(inst, cfg)
		if err != nil {
			return inst, err
		}
		c.instances[name] = newInst
		return newInst, nil
	}
	return nil, fmt.Errorf("Component Inst `%v` Not created", name)
}

func (c *Componentry) DestroyInstance(name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if inst, ok := c.instances[name]; ok {
		instConfig := c.instConfigs[name]
		err := c.components[instConfig.ID].Destroy(inst)
		if err == nil {
			delete(c.instConfigs, name)
			delete(c.instances, name)
			for i, v := range c.instNames {
				if v == name {
					c.instNames = append(c.instNames[:i], c.instNames[i+1:]...)
					break
				}
			}
		}
	}
	return fmt.Errorf("Component Inst `%v` Not created", name)
}

func (c *Componentry) DestroyAll() error {
	for i := len(c.instNames) - 1; i >= 0; i-- {
		err := c.DestroyInstance(c.instNames[i])
		if err != nil {
		}
	}
	return nil
}
